#include "cdevicectrl.h"

CDeviceCtrl::CDeviceCtrl(void *parent)
{
    this->parent = parent;
    video_buffers = NULL;
    m_BufferCount = 0;
    m_CaptureWidth = _CAPTURE_WIDTH;
    m_CaptureHeight = _CAPTURE_HEIGHT;
    pMedia = NULL;
    av_register_all();
}

CDeviceCtrl::~CDeviceCtrl()
{
    CloseDevice();
    delete CDebugLog::Instance();
}

int CDeviceCtrl::OpenDevice()
{
    CDebugLog::Instance()->DebugLog("Open Video Device!");
    pMedia = MediaInit();
    if(NULL == pMedia)
    {
        CDebugLog::Instance()->DebugLog("Open Video Device failed!");
        return -1;
    }

    m_bCaptureFlag=true;
    m_hDeviceHandle = -1;

    //init video device
    m_hDeviceHandle = -1;
    m_hDeviceHandle = v4l2_open(_DEVICE_NAME, O_RDWR, 0);
    if (m_hDeviceHandle < 0) {
           CDebugLog::Instance()->DebugLog("Cannot open device , err:%s", strerror(errno));
           return -1;
    }
    struct v4l2_capability capability;
    CLEAR(capability);
    if(-1 == ioctl(m_hDeviceHandle, VIDIOC_QUERYCAP, &capability))
    {
        CDebugLog::Instance()->DebugLog(" VIDIOC_QUERYCAP failed, err:%s", strerror(errno));
        return -1;
    }
    if(0 == (capability.capabilities & V4L2_CAP_VIDEO_CAPTURE))
    {
        CDebugLog::Instance()->DebugLog("This capture card not suspend video cature, err:%s", strerror(errno));
        return -1;
    }

    struct v4l2_input input;
    CLEAR(input);
    input.index = 0;
    if(-1 == ioctl(m_hDeviceHandle, VIDIOC_ENUMINPUT, &input))
    {
        CDebugLog::Instance()->DebugLog("VIDIOC_ENUMINPUT, err:%s", strerror(errno));
        return -1;
    }
    if(-1 == ioctl(m_hDeviceHandle, VIDIOC_S_INPUT, &input))
    {
        CDebugLog::Instance()->DebugLog("VIDIOC_S_INPUT, err:%s", strerror(errno));
        return -1;
    }

    struct v4l2_format              fmt;
    CLEAR(fmt);
    ioctl(m_hDeviceHandle, VIDIOC_G_FMT, &fmt);

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width       = _CAPTURE_WIDTH;
    fmt.fmt.pix.height      = _CAPTURE_HEIGHT;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
    if(-1 == ioctl(m_hDeviceHandle, VIDIOC_S_FMT, &fmt))
    {
        CDebugLog::Instance()->DebugLog("VIDIOC_S_FMT, err:%s", strerror(errno));
        return -1;
    }



//    struct v4l2_frmsizeenum vfse;
//    vfse.pixel_format =  V4L2_PIX_FMT_YUYV;

//    while(-1 != ioctl(m_hDeviceHandle, VIDIOC_ENUM_FRAMESIZES, &vfse))
//    {
//        switch (vfse.type) {
//        case V4L2_FRMSIZE_TYPE_DISCRETE:
//            printf(" %ux%u",
//                   vfse.discrete.width, vfse.discrete.height);
//        break;
//        case V4L2_FRMSIZE_TYPE_CONTINUOUS:
//        case V4L2_FRMSIZE_TYPE_STEPWISE:
//            printf(" {%u-%u, %u}x{%u-%u, %u}",
//                   vfse.stepwise.min_width,
//                   vfse.stepwise.max_width,
//                   vfse.stepwise.step_width,
//                   vfse.stepwise.min_height,
//                   vfse.stepwise.max_height,
//                   vfse.stepwise.step_height);
//        }
//        vfse.index++;
//        fflush(stdout);
//    }


//    struct v4l2_fmtdesc vfd;
//    vfd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

//    while(!ioctl(m_hDeviceHandle, VIDIOC_ENUM_FMT, &vfd))
//    {
////        enum AVCodecID codec_id = fmt_v4l2codec(vfd.pixelformat);
////        enum AVPixelFormat pix_fmt = fmt_v4l2ff(vfd.pixelformat, codec_id);

//        vfd.index++;

//        if (!(vfd.flags & V4L2_FMT_FLAG_COMPRESSED))
//        {
//        } else if (vfd.flags & V4L2_FMT_FLAG_COMPRESSED)
//        {
//        } else {
//            continue;
//        }
//    }


    struct v4l2_requestbuffers req;
    CLEAR(req);
    req.count = 5;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    if (-1 == ioctl(m_hDeviceHandle, VIDIOC_REQBUFS, &req))
    {
        CDebugLog::Instance()->DebugLog("VIDIOC_REQBUFS, err:%s", strerror(errno));
        return -1;
    }

    struct v4l2_buffer buf;
    video_buffers = (VideoBuffer *)calloc(req.count, sizeof(*video_buffers));
    for (m_BufferCount = 0; m_BufferCount < req.count; ++m_BufferCount)
    {
        CLEAR(buf);

        buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory      = V4L2_MEMORY_MMAP;
        buf.index       = m_BufferCount;

        if(-1 == ioctl(m_hDeviceHandle, VIDIOC_QUERYBUF, &buf))
        {
            CDebugLog::Instance()->DebugLog("VIDIOC_QUERYBUF, err:%s", strerror(errno));
            return -1;
        }

        video_buffers[m_BufferCount].length = buf.length;
        video_buffers[m_BufferCount].start = mmap(NULL, buf.length,
                                                  PROT_READ | PROT_WRITE, MAP_SHARED,
                                                  m_hDeviceHandle, buf.m.offset);

        if (MAP_FAILED == video_buffers[m_BufferCount].start)
        {
            CDebugLog::Instance()->DebugLog("mmap faild, err:%s", strerror(errno));
            return -1;
        }
        //into the buffer queue
        if(-1 == ioctl(m_hDeviceHandle, VIDIOC_QBUF, &buf))
        {
            CDebugLog::Instance()->DebugLog("VIDIOC_QBUF, err:%s", strerror(errno));
            return -1;
        }
    }
    return 0;
}

int CDeviceCtrl::ReadAndDisplay()
{
    CDebugLog::Instance()->DebugLog("start video and audio thread");
    if(0 != pthread_create(&m_hThreadVideoCapture, NULL, ThreadVideoCapture, this))
    {
        CDebugLog::Instance()->DebugLog("start video thread failed.");
        return -1;
    }
    if(0 != pthread_create(&m_hThreadVideoEncode, NULL, ThreadVideoEncode, this))
    {
        CDebugLog::Instance()->DebugLog("start video thread failed.");
        return -1;
    }
    CDebugLog::Instance()->DebugLog("start video and audio thread.success");
    return 0;
}

int CDeviceCtrl::CloseDevice()
{
    CDebugLog::Instance()->DebugLog("Close Device.");
    //close video
    try{
        m_bCaptureFlag = false;
        if(0 != pthread_join(m_hThreadVideoCapture, NULL))
        {
            return 0;
        }
        if(0 != pthread_join(m_hThreadVideoEncode, NULL))
        {
            return 0;
        }
        enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ioctl(m_hDeviceHandle, VIDIOC_STREAMOFF, &type);
        for (unsigned int i = 0; i < m_BufferCount; ++i)
            v4l2_munmap(video_buffers[i].start, video_buffers[i].length);

        free(video_buffers);
        v4l2_close(m_hDeviceHandle);
    }catch(...){}
    m_BufferCount = 0;
    CDebugLog::Instance()->DebugLog("Close Device.end");
    return 0;
}


